-- Agent
-- Create by panyl
-- 压力测试-登录

require "socket"

Agent = {
    class = 0,
    state = GRID_STATE_COVER,
    x = 0,
    y = 0,
    type   = GRID_TYPE_EMPTY,
    hasKey = 0,
};
Agent.__index = Agent;

-- 构造函数
function Agent.new(account, ip, port)
    local self = {};
    setmetatable(self, Agent);

    -- 连接
    self:connect(ip, port);

    -- 绑定
    self:bindUser(account);

    return self;
end

-- 取得该对象的描述字符串
function Agent:get_ob_id()
    return string.format("Agent:%s:%s", tostring(self.ip), tostring(self.port));   
end

-- 绑定账号
function Agent:bindUser(userName)
    self.userName = userName;
end

-- 连接
function Agent:connect(ip, port)
    self:disconnect();

    self.ip   = ip;
    self.port = port;

    local client = socket.tcp();
    client:settimeout(0.00001);

    self.client = client;

    self.connected = false;

    self:checkConnected();

    return self:isConnected();
end

-- 断开连接
function Agent:disconnect()
    if self.client ~= nil then
        self.client:shutdown("both");
        self.client:close();
        self.client = nil;
        self.connected = false;
    end
end

-- 是否已经连接上了
function Agent:isConnected()
    if self.client ~= nil and self.connected then
        return true;
    end

    return false;
end

-- 检查连接
function Agent:checkConnected()
    -- 如果已经连接上了
    if self:isConnected() then
        return true;
    end

    local succ, status = self.client:connect(self.ip, self.port, "*", 0);
    if succ == 1 or status == "already connected" then
        -- 后续的操作都为非阻塞了
        self.client:settimeout(0);

        -- 标记为已经连接好
        self.connected = true;

        return true;
    end

    return false;
end

-- 发送数据
function Agent:send(data)
    -- 如果没连接
    if not self:isConnected() then
        return false;
    end

    -- TODO: 暂且认为一次能发完
    local result, err, send = self.client:send(Buffer.byte(data));
    if result == nil then
        -- 发送失败了
        trace("Socket", "发送失败，err:" .. err);

        if err == "closed" then
            self:disconnect();
        end

        return false;
    end

    return true;
end

-- 发送消息
function Agent:sendCmd(cmd, lpc)
    -- 如果没连接
    if not self:isConnected() then
        print(self.userName .. "没有连接。");
        return false;
    end

    local id = iif(type(cmd) == "string", MSG.getMsgId(cmd), cmd);

    -- 打包消息体
    local body = PktAnalyser.pack(id, lpc);

    -- 打包消息头
    local buffer = Buffer.create(0);
    Buffer.set8(buffer, 1, 77);                      -- M
    Buffer.set8(buffer, 2, 90);                      -- Z
    Buffer.setu16(buffer, 3, 0);                     -- checksum
    Buffer.setu32(buffer, 5, 0);                     -- tickcount
    Buffer.setu32(buffer, 9, 2 + Buffer.size(body)); -- 消息长度
    Buffer.set16(buffer, 13, 0);                     -- 对齐位
    Buffer.setu16(buffer, 15, id);                   -- 消息编号

    -- 附加消息体
    buffer = Buffer.append(buffer, body);

    self:send(buffer);

    -- 发送消息
    -- printTable(lpc, "发送消息");
end

-- 发送二进制消息（已经打包完毕了）
function Agent:sendRaw(cmd, body)
    local id = iif(type(cmd) == "string", MSG.getMsgId(cmd), cmd);

    -- 打包消息头
    local buffer = Buffer.create(0);
    Buffer.set8(buffer, 1, 77);                      -- M
    Buffer.set8(buffer, 2, 90);                      -- Z
    Buffer.setu16(buffer, 3, 0);                     -- checksum
    Buffer.setu32(buffer, 5, 0);                     -- tickcount
    Buffer.setu32(buffer, 9, 2 + Buffer.size(body)); -- 消息长度
    Buffer.set16(buffer, 13, 0);                     -- 对齐位
    Buffer.setu16(buffer, 15, id);                   -- 消息编号

    -- 附加消息体
    buffer = Buffer.append(buffer, body);

    -- 发送消息
    self:send(buffer);
end

-- 接收数据
function Agent:recv()
    if not self:isConnected() then
        return;
    end

    -- 读取数据
    local data;
    local readable, _, err = socket.select({ self.client }, nil, 0);
    if readable and #readable == 1 then
        local line, err = self.client:receive("*a");
        if err and err ~= "timeout" then
            -- 有错误，连接断开了
            cclog(self.userName .. "连接断开。");
            self:disconnect();
            return;
        elseif not err then
            data = Buffer.create(0);
            Buffer.setstr(data, 1, #line, line);
        end
    end

    if data then
        -- 收到消息
        CommandM.analyzeMsg(self, data);
    end
end
